#ifndef __MY_UTIL__
#define __MY_UTIL__
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <experimental/filesystem> //遍历目录文件 头文件  C++17针对于 文件的操作 的一个系统调用
#include <sys/stat.h>
#include <jsoncpp/json/json.h>
#include "bundle.h"

namespace cloud
{
	namespace fs = std::experimental::filesystem;
	class FileUtil
	{
	private:
		std::string _filename; // 文件或目录 的路径

	public:
		FileUtil(const std::string &filename) : _filename(filename) {}

		// 删除文件
		bool Remove()
		{
			if (Exists() == false)
			{
				return true;
			}
			remove(_filename.c_str());
			return true;
		}

		// 获取文件大小
		int64_t FileSize() // int64_t防止文件过大
		{
			// 取文件的属性的一个新接口
			struct stat st;
			if (stat(_filename.c_str(), &st) < 0) // stat(_filename.c_str(),&st)  一个新的接口：获取文件_filename的属性
			{
				std::cout << "get file size failed!\n";
				return -1;
			}
			return st.st_size;
		}

		// 获取文件最后一次修改时间
		time_t LastMTime() // time_t linux下相当于 long int
		{
			struct stat st;
			if (stat(_filename.c_str(), &st) < 0)
			{
				std::cout << "get file size failed!\n";
				return -1;
			}
			return st.st_mtime;
		}

		// 获取文件最后一次访问时间
		time_t LastATime()
		{
			struct stat st;
			if (stat(_filename.c_str(), &st) < 0)
			{
				std::cout << "get file size failed!\n";
				return -1;
			}
			return st.st_atime;
		}

		// 获取文件名字
		std::string FileName()
		{
			// 文件路径形式 ：./abc/test.txt   我们只想要最后的test.txt
			size_t pos = _filename.find_last_of("/");
			if (pos == std::string::npos)
			{ // 如果没有找到说明本身就是一个文件名 所以返回_filename
				return _filename;
			}
			return _filename.substr(pos + 1); // 找到了'/' 就从'/'下一个位置开始截到最后
		}

		// 获取（读）文件中指定位置指定长度的数据
		bool GetPosLen(std::string *body, size_t pos, size_t len)
		{
			size_t fsize = FileSize();
			if (pos + len > fsize)
			{
				std::cout << "get file len is error\n";
				return false;
			}

			std::ifstream ifs;
			ifs.open(_filename, std::ios::binary);
			if (ifs.is_open() == false)
			{
				std::cout << "read open file failed!\n";
				return false;
			}

			ifs.seekg(pos, std::ios::beg); // seekg 是跳转到指定位置(pos)
			body->resize(len);
			ifs.read(&(*body)[0], len);
			if (ifs.good() == false)
			{
				std::cout << "get file content failed\n";
				ifs.close();
				return false;
			}
			ifs.close();
			return true;
		}

		// 获取文件数据
		bool GetContent(std::string *body)
		{
			size_t fsize = FileSize();
			return GetPosLen(body, 0, fsize);
		}

		// 向文件中写入数据
		bool SetContent(const std::string &body)
		{
			std::ofstream ofs;
			ofs.open(_filename, std::ios::binary);
			if (ofs.is_open() == false)
			{
				std::cout << "write open file failed!\n";
				return false;
			}
			ofs.write(&body[0], body.size());
			if (ofs.good() == false)
			{
				std::cout << "write file content failed!\n";
				ofs.close();
				return false;
			}
			ofs.close();
			return true;
		}

		// 文件压缩
		bool Compress(const std::string &packname) // 将当前对象操作的文件（构造时指定的 _filename）压缩后保存到 packname 路径。
		{
			// 1. 获取源文件数据
			std::string body;
			if (GetContent(&body) == false) // GetContent(&body)会执行 然后再判断是否为false
			{
				std::cout << "compress get file content failed!\n";
				return false;
			}
			// 2. 对数据进行压缩
			std::string packed = bundle::pack(bundle::LZIP, body);
			// 3. 将压缩的数据写入到压缩包文件中
			FileUtil fu(packname); ////////////////////////////////// 创建一个操作工具对象 fu 它默认操作packname文件
			if (fu.SetContent(packed) == false)
			{
				std::cout << "compress write packed data failed!\n";
				return false;
			}
			return true;
		}

		// 文件解压缩
		bool UnCompress(const std::string &filename) // 将当前对象操作的压缩文件（构造时指定的 _filename）解压到 filename 路径
		{
			// 1.将当前压缩包数据读取出来
			std::string body;
			if (GetContent(&body) == false)
			{
				std::cout << "uncompress get file content failed!\n";
				return false;
			}
			// 2.对压缩的数据进行解压缩
			std::string unpacked = bundle::unpack(body);
			// 3.将解压缩的数据写入到新文件
			FileUtil fu(filename);
			if (fu.SetContent(unpacked) == false)
			{
				std::cout << "uncompress write packed data failed!\n";
				return false;
			}
			return true;
		}

		// 判断文件是否存在
		bool Exists()
		{
			return fs::exists(_filename); // 上面的 namespace fs = std::experimental::filesystem;   C++17目录遍历 直接调用接口
		}

		// 创建目录
		bool CreateDirectory()
		{
			if (Exists())
			{
				return true; // 已经存在就不需要创建
			}
			return fs::create_directories(_filename); // 不存在就创建一个目录
		}

		// 浏览遍历目录 获取目录下的文件信息  遍历目录下的所有文件（不含子目录），将相对路径存入 array
		bool ScanDirectory(std::vector<std::string> *array) // array就相当于是一个目录
		{
			for (auto &e : fs::directory_iterator(_filename)) // 直接范围for遍历目录  _filename指的是要进行文件扫描操作的目标目录的路径，函数会基于这个路径去遍历目录，获取其中的文件信息
			{
				if (fs::is_directory(e) == true) // 如果遍历中 是目录文件(文件夹) 就不对其操作直接跳过
				{
					continue;
				}
				// relative_path 带有路径的文件名(相对路径)
				array->push_back(fs::path(e).relative_path().string());
			}
			return true;
		}
	};

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	class JsonUtil
	{
	public:
		// 序列化
		static bool Serialize(const Json::Value &root, std::string *str)
		{
			Json::StreamWriterBuilder swb;
			std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
			std::stringstream ss;
			if (sw->write(root, &ss) != 0)
			{
				std::cout << "json write failed!\n";
				return false;
			}
			*str = ss.str();
			return true;
		}

		// 反序列化
		static bool UnSerialize(const std::string &str, Json::Value *root)
		{
			Json::CharReaderBuilder crb;
			std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
			std::string err;
			bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);
			if (ret == false)
			{
				std::cout << "parse error: " << err << std::endl;
				return false;
			}
			return true;
		}
	};
}
#endif
